package com.fa4j.product.stock.service.impl;

import com.fa4j.common.base.exception.CommonRequestError;
import com.fa4j.common.base.exception.CommonServerError;
import com.fa4j.product.stock.convert.StockConverter;
import com.fa4j.product.stock.model.*;
import com.fa4j.product.stock.repository.StockRecordRepository;
import com.fa4j.product.stock.repository.StockRepository;
import com.fa4j.product.stock.service.StockService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 服务实现-商品库存服务
 *
 * @since 2025-11-24 14:52:24
 */
@Slf4j
@Service
@Validated
@RequiredArgsConstructor
public class StockServiceImpl implements StockService {

    private final StockRepository stockRepository;
    private final StockRecordRepository stockRecordRepository;

    private final StockConverter stockConverter;

    /**
     * 保存库存
     *
     * @param cmd 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveStock(@Valid StockSaveCmd cmd) {
        List<Stock> stockList = stockRepository.queryByItemId(cmd.getItemId());
        String bizNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
        List<StockRecord> recordList = new ArrayList<>();
        for (StockSaveCmd.SpecStock specStock : cmd.getSpecStockList()) {
            Stock stock = stockList.stream().filter(s -> Objects.equals(s.getSpecId(), specStock.specId())).findFirst().orElse(null);
            BigDecimal changeQuantity = specStock.quantity();
            if (stock == null) {
                stock = new Stock();
                stock.setItemId(cmd.getItemId());
                stock.setSpecId(specStock.specId());
                stock.setQuantity(changeQuantity);
                stock.setLockQuantity(BigDecimal.ZERO);
                stock.setTotalQuantity(changeQuantity);
                stock.setOperatorId(cmd.getOperatorId());
                stockRepository.create(stock);
            } else {
                changeQuantity = specStock.quantity().subtract(stock.getQuantity());
                if (changeQuantity.compareTo(stock.getLockQuantity()) < 0) {
                    throw CommonRequestError.BAD_REQUEST.build("库存数量[" + specStock.quantity() + "]小于锁定库存" + stock.getLockQuantity());
                }
                stock.setQuantity(specStock.quantity());
                stock.setTotalQuantity(stock.getTotalQuantity().add(changeQuantity));
                stock.setOperatorId(cmd.getOperatorId());
                stockRepository.modify(stock);
            }
            StockRecord stockRecord = stockConverter.toStockRecord(stock);
            stockRecord.setBizType(StockBizType.STOCK_SETTING);
            stockRecord.setBizNo(bizNo);
            stockRecord.setChangeQuantity(changeQuantity);
            stockRecord.setRemark(StockBizType.STOCK_SETTING.getName());
            recordList.add(stockRecord);
        }
        //保存库存记录
        stockRecordRepository.batchCreate(recordList);
    }


    /**
     * 锁定订单库存
     *
     * @param cmd 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<StockOrderLockItem> lockOrderStock(@Valid StockOrderLockCmd cmd) {
        List<Integer> specIds = cmd.getOrderItemList().stream().map(StockOrderLockCmd.OrderItem::specId).toList();
        List<Stock> stockList = stockRepository.queryBySpecIds(specIds);
        List<StockRecord> recordList = new ArrayList<>();
        List<StockOrderLockItem> resultItemList = new ArrayList<>();
        for (StockOrderLockCmd.OrderItem orderItem : cmd.getOrderItemList()) {
            StockOrderLockItem lockItem = new StockOrderLockItem();
            lockItem.setItemId(orderItem.itemId());
            lockItem.setSpecId(orderItem.specId());
            lockItem.setQuantity(orderItem.quantity());
            resultItemList.add(lockItem);
            Stock stock = stockList.stream()
                    .filter(s -> Objects.equals(s.getItemId(), orderItem.itemId()) && Objects.equals(s.getSpecId(), orderItem.specId()))
                    .findFirst().orElse(null);
            if (stock != null && stock.getLockQuantity().add(orderItem.quantity()).compareTo(stock.getQuantity()) <= 0) {
                lockItem.setStockId(stock.getId());
                stock.setLockQuantity(stock.getLockQuantity().add(orderItem.quantity()));
                stock.setOperatorId(cmd.getOperatorId());
                StockRecord stockRecord = stockConverter.toStockRecord(stock);
                stockRecord.setBizType(StockBizType.ORDER_LOCK);
                stockRecord.setBizNo(cmd.getOrderId());
                stockRecord.setRemark(StockBizType.ORDER_LOCK.getName());
                stockRecord.setChangeQuantity(orderItem.quantity());
                recordList.add(stockRecord);
            }
        }
        List<StockOrderLockItem> noStockItemList = resultItemList.stream().filter(i -> i.getStockId() == null).toList();
        if (!noStockItemList.isEmpty()) {
            //库存不足
            throw StockError.NOT_ENOUGH.build(noStockItemList);
        }
        for (Stock stock : stockList) {
            if (!stockRepository.modify(stock)) {
                throw StockError.NOT_ENOUGH.build();
            }
        }
        stockRecordRepository.batchCreate(recordList);
        return resultItemList;
    }

    /**
     * 出库订单库存
     *
     * @param cmd 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void outOrderStock(@Valid StockOrderOutCmd cmd) {
        List<StockRecord> recordList = stockRecordRepository.queryByBizNo(cmd.getOrderId(), List.of(StockBizType.ORDER_OUT, StockBizType.ORDER_LOCK));
        if (recordList.stream().anyMatch(r -> r.getBizType().equals(StockBizType.ORDER_OUT))) {
            log.warn("订单{}已经出库", cmd.getOrderId());
            return;
        }
        List<StockRecord> lockRecordList = recordList.stream().filter(r -> r.getBizType().equals(StockBizType.ORDER_LOCK)).toList();
        List<Stock> stockList = stockRepository.queryByIds(lockRecordList.stream().map(StockRecord::getStockId).toList());

        List<StockRecord> outRecordList = new ArrayList<>();
        for (StockRecord outRecord : lockRecordList) {
            Stock stock = stockList.stream()
                    .filter(s -> Objects.equals(s.getId(), outRecord.getStockId()))
                    .findFirst()
                    .orElseThrow(() -> CommonServerError.INTERNAL_SERVER_ERROR.build("未找到库存数据", null));
            stock.setOperatorId(cmd.getOperatorId());
            stock.setQuantity(stock.getQuantity().subtract(outRecord.getChangeQuantity()));
            stock.setLockQuantity(stock.getLockQuantity().subtract(outRecord.getChangeQuantity()));

            StockRecord stockRecord = stockConverter.toStockRecord(stock);
            stockRecord.setBizType(StockBizType.ORDER_OUT);
            stockRecord.setBizNo(cmd.getOrderId());
            stockRecord.setRemark(StockBizType.ORDER_OUT.getName());
            stockRecord.setChangeQuantity(outRecord.getChangeQuantity().negate());
            outRecordList.add(stockRecord);
        }

        for (Stock stock : stockList) {
            if (!stockRepository.modify(stock)) {
                throw CommonServerError.INTERNAL_SERVER_ERROR.build("库存出库更新失败", null);
            }
        }
        stockRecordRepository.batchCreate(outRecordList);

    }

    /**
     * 取消订单库存
     *
     * @param cmd 参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrderStock(@Valid StockOrderCancelCmd cmd) {
        List<StockRecord> recordList = stockRecordRepository.queryByBizNo(cmd.getOrderId(), List.of(StockBizType.ORDER_CANCEL, StockBizType.ORDER_LOCK));
        if (recordList.stream().anyMatch(r -> r.getBizType().equals(StockBizType.ORDER_CANCEL))) {
            log.warn("订单{}已经取消", cmd.getOrderId());
            return;
        }
        List<StockRecord> lockRecordList = recordList.stream().filter(r -> r.getBizType().equals(StockBizType.ORDER_LOCK)).toList();
        List<Stock> stockList = stockRepository.queryByIds(lockRecordList.stream().map(StockRecord::getStockId).toList());

        List<StockRecord> cancelRecordList = new ArrayList<>();
        for (StockRecord outRecord : lockRecordList) {
            Stock stock = stockList.stream()
                    .filter(s -> Objects.equals(s.getId(), outRecord.getStockId()))
                    .findFirst()
                    .orElseThrow(() -> CommonServerError.INTERNAL_SERVER_ERROR.build("未找到库存数据", null));
            stock.setOperatorId(cmd.getOperatorId());
            stock.setLockQuantity(stock.getLockQuantity().subtract(outRecord.getChangeQuantity()));

            StockRecord stockRecord = stockConverter.toStockRecord(stock);
            stockRecord.setBizType(StockBizType.ORDER_CANCEL);
            stockRecord.setBizNo(cmd.getOrderId());
            stockRecord.setRemark(StockBizType.ORDER_CANCEL.getName());
            stockRecord.setChangeQuantity(outRecord.getChangeQuantity().negate());
            cancelRecordList.add(stockRecord);
        }

        for (Stock stock : stockList) {
            if (!stockRepository.modify(stock)) {
                throw CommonServerError.INTERNAL_SERVER_ERROR.build("库存出库更新失败", null);
            }
        }
        stockRecordRepository.batchCreate(cancelRecordList);

    }
}